/*
 * Copyright (c) 2007 empronix (http://www.empronix.com)
 * Author: Benedikt Sauter <sauter@empronix.com>
 * All rights reserved.
 *
 * Short descripton of file:
 *
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution.
 *   * Neither the name of the FH Augsburg nor the names of its 
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES{} LOSS OF USE, 
 * DATA, OR PROFITS{} OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "common.h"
#include "protocol.h"
#include "adc.h"
#include "io.h"
#include "pwm.h"

#include <avr/interrupt.h>

volatile unsigned int counter;

 SIGNAL(SIG_OUTPUT_COMPARE1A){
    //OCR1A = counter;
 }



void pwm_parser(char *buf)
{
   switch(buf[0])
   {
      case CMD_PWM_INIT_PIN:
         pwm_init_usb((int)buf[2]);
      break;
      case CMD_PWM_DEINIT_PIN:
         pwm_deinit_usb((int)buf[2]);
      break;
      case CMD_PWM_SPEED:
         pwm_speed_usb((int)buf[2],(int)buf[3]);
      break;
      case CMD_PWM_VALUE:
        pwm_value_usb((int)buf[2],(int)buf[3]);
      break;

      default:
         answer[0]=buf[0];
         answer[1]=RSP_UNKOWN_CMD;
         answer[2]=0;
         CommandAnswer(3);
    }
}

int pwm_init(int pin)
{
   if (pin >= 8 && pin <= 9)
   {
      octopus.ports[pin] = PIN_PWM;

      switch(pin)
      {
         case 9:	        
            /* set pin as output */
            DDRB |=(1<<PB5); 
            OCR1A=1; // TOP          
#ifdef OCTOPUS_CAN
            /* configure timer, (WGM = FAST PWM) (clear on OCnA, set on TOP  COM1A1)*/    
            TCCR1A = (1<<WGM10)|(1<<COM1A1);         
            TCCR1B = (1<<WGM12)|(1<<CS10);  // system clock
#else            
            /* configure timer */    
            //TCCR1 = (1<<WGM10)|(1<<COM01)|(1<<CS01);                        
            //TCCR1B = (1<<CS10);  // system clock
#endif
         break;
         case 8:	  
            DDRB |=(1<<PB4); 
#ifdef OCTOPUS_CAN
            OCR2A=1; // TOP          
            /* configure timer, (WGM = FAST PWM) (clear on OCnA, set on TOP  COM1A1)*/    
            TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS20);         
            //TCCR1B = (1<<WGM12)|(1<<CS10);  // system clock
#else            
            /* configure timer */    
            //TCCR1 = (1<<WGM10)|(1<<COM01)|(1<<CS01);                        
            //TCCR1B = (1<<CS10);  // system clock
#endif
         break;
         default:
	         return RSP_IMPOSSIBLE_PIN_CONFIG;
      } 
      return RSP_OK; 
   }
   return RSP_UNKOWN_PIN;
}

void pwm_init_usb(int pin)
{
   answer[0]=CMD_PWM_INIT_PIN;
   answer[1]= (unsigned char)pwm_init(pin);
   answer[2]=0;

   CommandAnswer(3);
}



void pwm_deinit_usb(int pin)
{
  answer[0]=CMD_PWM_DEINIT_PIN;
  answer[1]=(unsigned char)pwm_deinit(pin);
  answer[2]=0;
  CommandAnswer(3);
}

int pwm_deinit(int pin)
{
  octopus.ports[pin]=PIN_NONE;
#ifdef OCTOPUS_CAN
  switch(pin)
  {
     case 8: TCCR2A = 0; break;
     case 9: TCCR1B = 0; TCCR1A = 0; break;
     default: return RSP_WRONG_PIN_CONFIG;

  }     
  io_set_pin_direction_out(pin);
#endif
  return RSP_OK;
}



void pwm_speed_usb(int pin, int speed)
{
   answer[0]=CMD_PWM_SPEED;
   answer[1]=pwm_speed(pin,speed);
   answer[2]=0; 
   CommandAnswer(3);
}


int pwm_speed(int pin, int speed)
{
   if (octopus.ports[pin] == PIN_PWM)
   {
      switch (pin)
      {
         case 9: 
            switch (speed)
            {
               case 1:                  
                  TCCR1B = (1<<CS10);  // system clock      33 kHz                               
               break;
               case 2:
                  TCCR1B = (1<<CS11);  // div 8 // 500 Hz                 
               break;
               case 3:
                  TCCR1B = (1<<CS11)|(1<<CS10);  // div 64
               break;
               case 4:
                  TCCR1B = (1<<CS12);  // div 256
               break;
               case 5:
                  TCCR1B = (1<<CS12)|(1<<CS10);  // div 1024
               break;

               default:
               ;
            }
         break;
         case 8: 
#ifdef OCTOPUS_CAN

            switch (speed)
            {
               case 1:                  
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS20);     // external 32kHz                              
               break;
               case 2:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS21);    // external div 8                      
               break;
               case 3:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS20)|(1<<CS21); //div 32         
               break;
               case 4:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS22); //div 64         
               break;
               case 5:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS20)|(1<<CS22); //div 128         
               break; 
               case 6:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS21)|(1<<CS22); //div 256         
               break;   
               case 7:
                  TCCR2A = (1<<WGM20)| (1<<WGM21)|(1<<COM2A1)|(1<<CS21)|(1<<CS22)|(1<<CS20); //div 1024         
               break;                  
               default:
               ;
            }
#endif
         break;
         default:
	      return RSP_IMPOSSIBLE_PIN_CONFIG;
      } 
      return RSP_OK;
   } 
   else 
   {
      return RSP_WRONG_PIN_CONFIG;
   } 
}

void pwm_value_usb(int pin, unsigned int value)
{
   answer[0]=CMD_PWM_VALUE;
   answer[1]=pwm_value(pin,value);
   answer[2]=0; 
   CommandAnswer(3);
}


int pwm_value(int pin, unsigned int value)
{
   if (octopus.ports[pin] == PIN_PWM)
   {
      switch (pin)
      {
         case 9: 
            OCR1A = value; 
         break;
         case 8: 
#ifdef OCTOPUS_CAN
            OCR2A = value; 
#endif
            ; 
         break;
         default:
	      return RSP_IMPOSSIBLE_PIN_CONFIG;
      } 
      return RSP_OK;
   } 
   else 
   {
      return RSP_WRONG_PIN_CONFIG;
   } 
}
